from data import read_data
import numpy as np
from deap import base, creator, tools, gp,algorithms
import operator
import random


filename = r'100_5_20_9_D3.def'
resources_data, tasks_data, skill_num = read_data.read_dataset(filename)

# 转换为内部数据结构
resources = [{'id': r['id'], 'skills': r['skill']} for r in resources_data]
tasks = [{'id': t['id'], 'dur': t['duration'],
          'skill_req': (t['skill_type'], t['skill_level']),
          'preds': t['predecessors']} for t in tasks_data]

NUM_TASKS = len(tasks)
NUM_RESOURCES = len(resources)

# 构建技能匹配矩阵
skill_match = np.zeros((NUM_TASKS, NUM_RESOURCES), dtype=bool)
for i, task in enumerate(tasks):
    st, sl = task['skill_req']
    for j, res in enumerate(resources):
        if res['skills'][st] >= sl:
            skill_match[i, j] = True




def mutation(individual):
    # 确保列表长度至少为2
    if len(individual) < 2:
        return individual  # 如果列表太短，无法选择一段，直接返回原列表
    # 随机选择起始和结束索引
    start = random.randint(0, NUM_TASKS - 1)
    end = random.randint(start, NUM_TASKS - 1)
    segment = individual[start:end + 1]
    random.shuffle(segment)#打乱顺序
    # 将打乱后的段放回原列表
    individual[start:end + 1] = segment
    return creator.Individual(individual),
# 交叉函数
def cross(ind1,ind2):
    start = random.randint(0, NUM_TASKS - 1)
    end = random.randint(start, NUM_TASKS - 1)
    new1 = set(ind2[start:end+1])
    new2 = set(ind1[start:end+1])
    new_ind1 = set(ind1[:start])-new1 | new1 |(set(ind1[start:])-new1)
    new_ind2 = set(ind2[:start])-new2 | new2 |(set(ind2[start:])-new2)
    return creator.Individual(new_ind1), creator.Individual(new_ind2)

# 适应度评估函数
def evaluate(individual):
    task_schedule = []
    resource_load = {r['id']: [] for r in resources}
    task_end_time = {}
    # for task_id in individual:
    #     task = tasks[task_id]
    #     # 检查依赖,如果存在依赖未完成,直接放到末尾
    #     if any(p not in task_schedule for p in task['preds']):
    #         individual.remove(task_id)
    #         individual.append(task_id)
    i=0
    while i < len(individual):
        task = tasks[individual[i]]
        if any(p not in task_schedule for p in task['preds']):
            individual.append(individual[i])
            individual.pop(i)
        else:
            task_schedule.append(individual[i])
            i+=1
    task_schedule = []
    for task_id in individual:
        task = tasks[task_id]
        # 寻找可用资源
        available_resources = []
        for res_id, res in enumerate(resources):
            if skill_match[task_id, res_id]:
                available_resources.append(res_id)
        if not available_resources:
            print(f"No available resources for Task {task_id}")
            return (float('inf'),)
        # 选择最优资源（最快开始）
        best_res = min(available_resources, key=lambda x: resource_load[x][-1][1] if resource_load[x] else 0)
        # 开始时间由选定资源上一任务结束时间和前置任务结束时间综合确定
        resource_last_end = resource_load[best_res][-1][1] if resource_load[best_res] else 0
        max_pred_end = 0
        for pred in task['preds']:
            if pred in task_end_time:
                max_pred_end = max(max_pred_end, task_end_time[pred])
        start_time = max(resource_last_end, max_pred_end)
        end_time = start_time + task['dur']
        resource_load[best_res].append((task_id, end_time,start_time))
        # 添加到任务执行列表
        task_schedule.append((task_id, best_res, start_time, end_time))
        task_end_time[task_id] = end_time
    # 找出最大的结束时间即为结束时间
    makespan = max(t[3] for t in task_schedule)

    # print(makespan)
    return (makespan,)

# 个体生成
def dynamic_initRepeat(num_masks):
    """根据 num_masks 生成指定长度的个体"""
    elements = random.sample(range(num_masks), num_masks)
    return creator.Individual(elements)

# 遗传算法配置
def configure_ga():
    #  创建基础组件
    creator.create("FitnessMax", base.Fitness, weights=(-1.0,))
    creator.create("Individual", list, fitness=creator.FitnessMax)

    toolbox = base.Toolbox()
    toolbox.register("individual", lambda:dynamic_initRepeat(NUM_TASKS))
    toolbox.register('population', tools.initRepeat, list, toolbox.individual)
    toolbox.register("select", tools.selection.selTournament, tournsize=10)#轮盘赌选择函数
    toolbox.register("mate", cross)#交叉
    toolbox.register("mutate", mutation)  # 变异
    toolbox.register("evaluate", evaluate)#评估
    return toolbox



# 运行遗传算法
def main():
    toolbox = configure_ga()
    pop = toolbox.population(n=500)

    hof = tools.HallOfFame(1)
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("avg", np.mean)
    stats.register("std", np.std)
    stats.register("min", np.min)
    stats.register("max", np.max)

    algorithms.eaSimple(pop, toolbox, cxpb=0.7, mutpb=0.3, ngen=1000,
                        stats=stats, halloffame=hof, verbose=True)

    best = hof[0]
    print("\nBest Schedule:")
    # print(gp.pretty_print(best))
    print(f"Makespan: {best.fitness.values[0]}")


if __name__ == "__main__":
    main()
